import React from 'react';
import { Card, Checkbox, Avatar, Divider, Popover, Button, message } from 'antd'
import 'styles/components/template/waterfall-box/waterfall-box.scss'
import moment from 'moment'
import { messages } from "share/common";
import menuRoute from "routes/menuRoute";
import BitmapIMG from "images/components/Bitmap.png"
const { Meta } = Card;
/**
 * 瀑布流布局+懒加载组件 by:陈行健
 */
class WaterfallBox extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      loading: true,
      columnCount:0,
      page: 0,
      data:[],
      cacheData: [],
      pagination: {
        total: 0,
        current: 0,
        pageSize: 10
      },
      isLoaded: false, //判断是否到底了，到底了显示提示语句已经到底并且不再请求新页数据
      value: [],
      selectedRows:[],
      cardItems:[],
      columnHeights:[],
      updateCount: 0,
      updateCheckCount: 0,
      getListCount: 0
    };
    this._onScrollEvent = this._onScrollEvent.bind(this);
    this.columnHeights;
    this._container;
    this.checkedList = []; //多选框值列表，加载列表时push进去
  }

  componentWillMount(){
    this.setState({
      pagination: this.props.pagination,
      pageSize: this.props.pageSize,
      data: this.props.data,

    });
  }

  componentDidMount(){
    let pageWidth = this._container.clientWidth;
    let columns = parseInt(pageWidth / (this.props.cardWidth + this.props.gutter));
    this.columnHeights = Array.from({ length: columns }, () => 0);//第一次进入页面初始化基本数据
  }

  componentWillReceiveProps = (nextProps) => {
    this.setState({loading: true});
    // 接收父组件数据，当数据变化的时候重新接收赋值，因为不知道父组件中setState时是哪几个一起变化，这样写避免重复赋值或漏值
    if (!nextProps || nextProps === this.props) {
      this.setState({loading: false});
      return false;
    }else{
      if(!!nextProps.pagination){
        this.setState({
          pagination: nextProps.pagination
        });
      }
      if(!!nextProps.data){
        this.setState({
          data: nextProps.data
        });
      }
      if((nextProps.data !== this.props.data) && ((nextProps.pagination.current !== this.props.pagination.current) || nextProps.pagination.current === 1)){
        if(nextProps.pagination.current === 1){
          this.setState({
            selectedRows: Array.from(this.props.selectedRows),
            cacheData: nextProps.data
          });
        }else{
          let temp = this.state.cacheData;
          temp.splice(temp.length - this.props.pageSize, this.props.pageSize);
          this.setState({
            getListCount: 0,
            cacheData: [...temp, ...nextProps.data]
          });
        }
      }
    }
  };

  /**
   * shouldUpdate和WillUpdate里更新data会由于setState是异步的导致第一次渲染时数据滞后，所以主体数据应该放在WillReceiveProps或WillMount生命周期中
   * 如果传进来是同一批数据，不要做无意义渲染，否则会打乱columnHeights数组里存储的盒子高度
   * 但是只有再第一次渲染后才能拿到不同card高度计算布局，即拿到数据后需要渲染两遍才能正确展示布局
   * 所以getItemStyle时，columnHeights数组只能在第2次渲染时生效（updateCount：0第一次渲染；updateCount：1第二次渲染）第一次渲染后要重置
    */

  /**
   * 190105 究竟是有多傻逼我才会想在shouldUpdate和WillUpdate里更新data, 对生命周期的理解就不对
   */
  shouldComponentUpdate(newProps, newState) {
    if (newState.cacheData !== this.state.cacheData ) {
      this.setState({
        updateCount: 1
      });
      return true
    }
    if(newState.updateCount === 1){
      this.setState({
        updateCount: 0
      });
      return true
    }

    //组件内部选择数据变化引起的渲染||这个时候是已经拿到card的dom节点的 无需渲染两遍
    if(newState.selectedRows !== this.state.selectedRows || newState.isLoaded !== this.state.isLoaded ){
      return true
    }

    this.setState({
      updateCount: 0,
    });
    return false

  }

  componentWillUpdate(){
    this.columnHeights = Array.from({ length: this.columnHeights.length }, () => 0);//渲染后要重置，因为下一次拿数据还得重头渲染
    let box = document.getElementById('waterfall-box');  //更新盒子里的dom节点
    this.setState({
      cardItems: box.children,
      loading: false
    });

  }

  //根据值路径（obj.key1.key2）拿到值，多个值路径可以用“,”分开，返回值也将用“,”分开
  getValue = (item, objectSrc) => {
    let value = [];
    if(!!objectSrc){
      let key = objectSrc.split(",").length;
      for(let i = 0; i<key ; i++ ){
        let valueSrc = objectSrc.split(",")[i].split(".").length; //split().length一定大于等于1
        let valueName = item;
        for(let j = 0; j<valueSrc ; j++ ){
          valueName = valueName[objectSrc.split(",")[i].split(".")[j]] || "";
        }
        value.push(valueName);
      }
    }
    return value.join(",");
  };

  //拿到高度数组中的最小值
  getShortestColumn = () => {
    const minValue = Math.min(...this.columnHeights);
    return this.columnHeights.indexOf(minValue);
  };

  //依据高度数组里的最小值拿到布局样式（瀑布流就是优先找到最短的一列往上补）
  getItemStyle = (item,index) => {
    const {cardItems, updateCount} = this.state;
    const { cardWidth, gutter } = this.props;
    const shortestColumnIndex = this.getShortestColumn();
    const left = ( cardWidth + gutter ) * shortestColumnIndex;
    const top = this.columnHeights[shortestColumnIndex];
    const normalizedHeight =  (index < cardItems.length) && cardItems[index].offsetHeight || 0; //确保已经拿到card节点了，如果还没有，先置0
    // updateCount && ();
    this.columnHeights[shortestColumnIndex] += (normalizedHeight + (normalizedHeight && gutter));
    return {
      display: "inline-block",
      minWidth: "200px",
      width: `${this.props.cardWidth}px`,
      left: `${left}px`,
      top: `${top}px`,
      position: `absolute`
    };
  };

  //监听滚动事件，这里react监听滚动事件的方法值得xiaoxi
  _onScrollEvent = () => {
    const { current, total } = this.state.pagination;
    const {cardItems, pageSize, getListCount} = this.state;
    if(cardItems.length >= total ){
      this.setState({
        isLoaded: true
      });
    }else{
      //翻页的时候拿一次就好了，滚动的时候会不断触发此函数不用次次都拿
      if(getListCount === 0 && (this._container.scrollTop + this._container.clientHeight > cardItems[cardItems.length -1].offsetTop)){
        let arr = [];
        for (let i=0; i<this.props.pageSize; i++) {
          arr.push({loading: true})
        }
        this.setState({
          loading: true,
          cacheData: [...this.state.cacheData, ...arr],
          updateCount: 0,
          getListCount: 1
        });
        this.props.getList(current, pageSize);//current 永远==== page+1
      }
    }
  };

  onChange = (value) => {
    const {selectedRows} = this.state;
    const {maxNum} = this.props;
    if(selectedRows.length < maxNum){
      this.setState({
        selectedRows: value
      });
      this.props.onChange(value);
    }else{
      message.warning(messages('components.key668',{arg1:this.props.maxNum}/*最多可选中{arg1}条*/));
    }
  };

  onCheckNoneChange= () => {
    this.setState({
      selectedRows: []
    });
    this.props.onChange([]);
  };

  onCheckAllChange = () => {
    const {cardItems, selectedRows} = this.state;
    const {maxNum} = this.props;
    let checkedRows = [];
    if(cardItems.length <= maxNum){ //已加载的数据还没有限制数量多，直接选中所有已加载数据
      this.setState({
        selectedRows: this.checkedList
      });
      checkedRows = this.checkedList;
    }else{ //已加载数据已经大于限制数了，从头开始选中剩余还能选的列表
      if(selectedRows.length < maxNum){
        this.setState({
          selectedRows: [...selectedRows, ...this.checkedList.slice(0,maxNum - selectedRows.length)]
        });
        checkedRows = [...selectedRows, ...this.checkedList.slice(0,maxNum - selectedRows.length)];
      }else{
        message.warning(messages('components.key668',{arg1:this.props.maxNum}/*最多可选中{arg1}条*/));
      }
    }
    this.props.onChange(checkedRows);
  };

  handleClick = (value) => {
    this.props.handleClick(value);
  };

  goBack = () => {
    let backUrl = this.props.backUrlKey && menuRoute.getRouteItem(this.props.backUrlKey, 'key').url;
    this.context.router.push(backUrl);
  };

  render() {
    const { pagination, selectedRows, pagination:{current, pageSize},cacheData, isLoaded} = this.state;
    this.checkedList = [];
    return (
      <div  className="waterfall-box" >
        {
          Number(pagination.total) > 0 ?
          <div className="table-header">
            {
              this.props.checkable &&  <div className="table-header-title">
                {messages('common.total', {total: pagination.total})/*共搜索到 {total} 条数据*/}，
                {messages('common.total.selected', {total: selectedRows.length})/*已选 {total} 条*/}，
                {messages('components.key668',{arg1:this.props.maxNum}/*最多可选中{arg1}条*/)}
              </div>
            }
            <div className="table-header-buttons">
              { this.props.checkable && <Button type="primary" onClick={this.onCheckAllChange}>{messages('components.key557'/*全选*/)}</Button>}
              { this.props.checkable && <Button onClick={this.onCheckNoneChange}>{messages('components.key558'/*取消已选*/)}</Button>}
              <Button onClick={this.goBack}>{this.props.backTitle}</Button>
            </div>
          </div>
            :
            <Button onClick={this.goBack}>{this.props.backTitle}</Button>
        }
        <Checkbox.Group onChange={this.onChange} value={selectedRows}>
          {
            <div id="waterfall-box" style={{position: "relative", height: this.props.checkable ? "calc(100vh - 223px)" : "calc(100vh - 203px)"}}
                 ref={c => this._container = c}
                 onScrollCapture={() => this._onScrollEvent()}>
              {pagination.total === 0 &&
                <div className="null-data">
                  <img src={BitmapIMG} height={160} />
                  <p>{messages('components.key722')/*暂无数据*/}</p>
                </div>}
              {cacheData && cacheData.map((item, index) => {
                const valueKey = this.getValue(item, this.props.valueKey);
                this.checkedList.push(valueKey);
                return(
                  <Card key={index}
                        loading={item.loading || false}
                        style={this.getItemStyle(item, index)}
                        hoverable={this.props.hoverable}
                        extra={this.props.checkable && !item.loading && <Checkbox value={valueKey}/>}
                        title={
                          this.props.title === "MATA"
                            ?
                            <Meta
                              avatar={<Avatar src={this.getValue(item, this.props.MetaSrc)}/>}
                              title={this.getValue(item, this.props.MetaTitle)}
                              description={this.getValue(item, this.props.MetaDescription)}
                              className={this.props.MetaClassName}
                            />
                            :
                            ( this.props.isTitleStatic
                                ?
                                this.props.title
                                :
                                this.getValue(item, this.props.MetaDescription)
                            )
                        }
                  >
                    <div onClick={() => this.handleClick(valueKey)}>
                      <div>
                        {
                          /*如果设置头像信息了显示头像信息*/
                          this.props.isMeta && <Meta
                            avatar={<Avatar src={this.getValue(item, this.props.MetaSrc)}/>}
                            title={this.getValue(item, this.props.MetaTitle)}
                            description={this.getValue(item, this.props.MetaDescription)}
                            className={this.props.MetaClassName}
                          />
                        }
                        {
                          /*展示主内容区信息*/
                          this.props.content && this.props.content.length && <div className="content">
                            {
                              this.props.content.map((self, index) => {
                                return(
                                  <p className={self.className} key={index}>
                                    <span>{self.title}:&nbsp;</span>
                                    {!self.dataType && <span>{this.getValue(item, self.dataIndex)}</span>}
                                    {!!self.dataType && self.dataType === "DATE" && <span>{moment(this.getValue(item, self.dataIndex)).format('YYYY-MM-DD')}</span>}
                                    {!!self.dataType && self.dataType === "DATETIME" && <span>{moment(this.getValue(item, self.dataIndex)).format('YYYY-MM-DD hh:mm:ss')}</span>}
                                    {!!self.dataType && self.dataType === "MONEY" && <span>{(self.showCode ? `${this.getValue(item, self.codeIndex)} ${this.filterMoney(this.getValue(item, self.dataIndex),2,true)}` : this.filterMoney(this.getValue(item, self.dataIndex),2,true))}</span>}
                                  </p>
                                )
                              })
                            }
                          </div>
                        }
                      </div>
                      {
                        /*如果设置扩展区域了并且扩张区域有值 显示分割线*/
                        !!this.props.description && !!this.getValue(item, this.props.description) && <Divider style={{margin: "8px 0"}}/>
                      }
                      <p className="description">
                        {this.getValue(item, this.props.description).length > 118
                          ?
                          <Popover content={this.getValue(item, this.props.description)}>
                            {`${this.getValue(item, this.props.description).substring(0,118)}...`}
                          </Popover>
                          :
                          this.getValue(item, this.props.description)
                        }
                      </p>
                    </div>
                  </Card>
                );
              })}
              {isLoaded && <Divider className="loaded" style={{
                marginTop: "10px",
                width: "100%",
                fontSize: "14px",
                top: `${ Math.max(...this.columnHeights) + 20}px`,
                position: `absolute`
              }}>{messages('components.key559'/*已经到底了*/)}</Divider>}
            </div>
          }
        </Checkbox.Group>

      </div>
    );
  }
}

WaterfallBox.contextTypes = {
  router: React.PropTypes.object
};

/**
 * content请遵循以下格式
 * title: 信息字段名
 * dataIndex: 可以拿到字段值的路径
 * dataType: 值类型，默认string，若为DATE做('YYYY-MM-DD')处理；若为DATETIME,做('YYYY-MM-DD hh:mm:ss')处理，若为MONEY，做filterMoney金额过滤
 * showCode：当值类型为MONEY时，选择是否显示该字段值的货币符号
 * codeIndex：币符号字段值的路径
 * className: 该行信息展示样式，将包裹在一条展示信息的DOM行上
 */
// content:[
//   {title: messages('finance.view.search.documentNo'/*单号*/), dataIndex: 'businessCode', className: ''},
//   {title: messages('finance.view.search.submitDate'/*提交日期*/), dataIndex: 'submittedDate', dataType: "DATE" , className: ''},
//   {title: messages('request.bill.name'/*单据名称*/), dataIndex: 'formName',className: ''},
//   {title: messages('borrowing.limit.control.amount'/*金额*/), dataIndex: 'originCurrencyTotalAmount', dataType: "MONEY", showCode: true, codeIndex:"originCurrencyCode", className: ''},
//   {title: messages('request.base.amount'/*本币金额*/), dataIndex: 'totalAmount', dataType: "MONEY" ,showCode: true, codeIndex:"currencyCode", className: ''}
// ]

WaterfallBox.propTypes = {
  data: React.PropTypes.array, //存放每一页数据
  getList: React.PropTypes.func.isRequired, //下拉加载数据的回调函数
  valueKey: React.PropTypes.string.isRequired, //列表key，也是选择回调返回的值 为可以拿到字段值的路径，若需要多个值，用“,”分开，返回的值也将用“,”分开
  backUrlKey: React.PropTypes.string.isRequired, //返回页面的routerKey
  backTitle: React.PropTypes.string, //返回按钮的文案
  selectedRows: React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.object]), //已选择列表
  pagination: React.PropTypes.object, //管理分页数据  {total: num ,current: num}
  pageSize:  React.PropTypes.number, //每页加载的数量
  cardWidth: React.PropTypes.number, //卡片项的宽度，请设置不低于200px的值，默认200px，将影响每行列数
  gutter: React.PropTypes.number, //卡片间隙宽度
  isTitleStatic: React.PropTypes.bool, //标题是否是静态字符串, 为true时，每一项card标题都相同,为title属性的值，否则为title属性对应字段的动态值
  description: React.PropTypes.string, //卡片扩展内容， 根据传入的data可以拿到该值的路径
  isMeta: React.PropTypes.bool, //是否有头像信息即Meta标签， 和title === "MATA"不同，当isMeta为true时，头像信息显示在主内容区
  content:React.PropTypes.array, //主内容区信息
  actionsFuc: React.PropTypes.array, //底部操作区的绑定事件，请和actions对应的图标顺序一致且长度相等，若无请传null代替
  handleClick: React.PropTypes.func,  //点击单个card时的回调
  checkable: React.PropTypes.bool, //是否支持多选
  maxNum: React.PropTypes.number,  //最多选择多少条数据，在checkable为true时生效
  onChange: React.PropTypes.func,  //进行选择后的回调, 在checkable为true时生效

  // 以下参见antd Card参数
  extra: React.PropTypes.oneOfType([React.PropTypes.func, React.PropTypes.string]), //在checkable为true时失效，且为多选框
  hoverable: React.PropTypes.bool, //hover时是否有box-shadow效果
  loading: React.PropTypes.bool,
  title: React.PropTypes.oneOfType([React.PropTypes.func, React.PropTypes.string]), //这里定义当title === "MATA",在title里显示Meta，相关属性如下

  //以下参见antd Card.Meta 参数
  MetaSrc: React.PropTypes.string,  //根据传入的data可以拿到该值的路径 例"approver.fullName"
  MetaTitle: React.PropTypes.string,
  MetaDescription: React.PropTypes.string,
  MetaClassName: React.PropTypes.string,
  MetaStyle: React.PropTypes.object
};

WaterfallBox.defaultProps = {
  valueKey: "",
  cardWidth: 210,
  pageSize: 10,
  pagination: {
    total: 0,
    current: 0,
  },
  selectedRows: [],
  gutter: 10,
  isTitleStatic: true,
  checkable: false,
  maxNum: 20,
  description: "",
  isMeta: false,
  hoverable: true,
  backTitle: messages("common.back")
};

export default WaterfallBox;
